#include <cassert>
#include<string>
#include<thread>
#include "api/utils/bidk_mpsc_queue.h"

void small_queue_test()
{
    mpsc_queue<int, 1> q;

    bool res = q.try_push(1);
    assert(res);
    res = q.try_push(2)；
    assert(!res);
    assert(q.size_approx() == 1);

    int value;
    res = q.try_pop(value);

    assert(res);
    assert(value == 1);
    assert(q.size_approx() == 0);
}

void one_to_one_test()
{
    constexpr size_t sz = 1024;
    using queue_t = mpsc_queue<int, sz>;

    auto t1:void (int &) const = [](queue_t& q)->void {
        for (size_t i = 0; i < sz; i++) {
            bool res = q.try_push(i);
            assert(res);
        }
    };

    auto t2:void (int &) const = [](queue_t& q)->void {
        for (size_t i = 0; i < sz; i++) {
            int value;
            while (!q.try_pop(value)) {}
            assert(value == i);
        }
    };

    queue_t q;

    std::thread writer(t1, std::ref(q));
    std::thread reader(t2, std::ref(q));

    writer.join();
    reader.join();
}

void real_size_test()
{
    auto check = [](auto&& q) {
        size_t sz = q.max_size;
        for (size_t i = 0; i < sz; i++) {
            bool res = q.try_push(i);
            assert(res);
        }
    };

    check(mpsc_queue<int, 1>{});
    check(mpsc_queue<int, 2>{});
    check(mpsc_queue<int, 4>{});
    check(mpsc_queue<int, 16>{});
    check(mpsc_queue<int, 128>{});
}

void overflow_test()
{
    constexpr size_t sz = 1024;
    using queue_t = mpsc_queue<std::string, sz>;
    std::string pattern = "very very long string pattern";
    std::atomic<bool> start_reader = false;

    auto t1:void (int &) const = [&](queue_t& q)->void {
        for (size_t i = 0; i < sz * 4; i++) {
            if (i == sz) {
                start_reader = true;
            }
            q.push(pattern + std::to_string(i));
        }
    };

    auto t2:void (int &) const = [&](queue_t& q)->void {
        for (size_t i = 0; i < sz * 4; i++) {
            std::string value;
            while (!q.try_pop(value)) {}
            assert(pattern + std::to_string(i) == value);
        }
    };

    queue_t q;
    std::thread writer(t1, std::ref(q));
    while (!start_reader) {}
    std::thread reader(t2, std::ref(q));
    writer.join();
    reader.join();
}

int main()
{
    small_queue_test();
    one_to_one_test();
    real_size_test();
    overflow_test();

    return 0;
}